package com.example.widgethost;

import android.app.Activity;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class WidgetHelper {
    private final static String TAG = "WidgetHelper";
    public static final int REQUEST_PICK_APPWIDGET = 20481;
    public final int REQUEST_CREATE_APPWIDGET = 20482;
    public final int REQUEST_BIND_APPWIDGET = 20483;
    private static volatile WidgetHelper sInstance = null;
    private AppWidgetManager mAppWidgetManager;
    private LauncherAppWidgetHost mAppWidgetHost;
    private final Context mContext;
    private final SharedPreferences mPrefs;
    private Gson mGson;
    private Activity activity;

    private WidgetHelper(Context context) {
        if (context instanceof Activity) {
            activity = (Activity) context;
        }
        mContext = context.getApplicationContext();
        mPrefs = context.getSharedPreferences("widgets_added", Context.MODE_PRIVATE);
        mGson = new GsonBuilder().create();
        setupHost();
        for (int wid : mAppWidgetHost.getAppWidgetIds()) {
            AppWidgetProviderInfo provider = mAppWidgetManager.getAppWidgetInfo(wid);
            if (provider == null) continue;
            Log.d(TAG, "Widget is allocated: " + provider.provider);
        }
    }

    public static WidgetHelper getSingleton(Context context) {
        if (sInstance == null) {
            synchronized (WidgetHelper.class) {
                if (sInstance == null) {
                    sInstance = new WidgetHelper(context);
                }
            }
        }
        return sInstance;
    }

    private void setupHost() {
        Log.e(TAG, "setupHost");
        mAppWidgetManager = AppWidgetManager.getInstance(mContext);
        mAppWidgetHost = new LauncherAppWidgetHost(mContext.getApplicationContext());
        mAppWidgetHost.startListening();
    }

    public List<AppWidgetHostView> getWidgets(Activity activity, String flattenComponentName) {
        ComponentName componentName = ComponentName.unflattenFromString(flattenComponentName);
        return getWidgets(activity, componentName);
    }

    public List<AppWidgetHostView> getWidgets(Activity activity, ComponentName componentName) {
        List<AppWidgetHostView> appWidgetHostViews = new ArrayList<>();
        List<Integer> ids = getWidgetIds(componentName);
        for (int id : ids) {
            AppWidgetHostView appWidgetHostViewTemp = getWidget(activity, componentName, id);
            appWidgetHostViews.add(appWidgetHostViewTemp);
        }
        return appWidgetHostViews;
    }

    private AppWidgetHostView getWidget(Activity activity, ComponentName componentName, int id) {
        AppWidgetHostView hostView = null;
        if (id != -1) {
            Log.d(TAG, "loading widget from id " + componentName);
            hostView = createWidgetFromId(id);
            if (hostView == null) {
                removeWidget(componentName, id);
                return null;
            }
        }

        if (hostView == null) {
            Log.d(TAG, "creating new widget " + componentName);
            hostView = loadWidget(activity, componentName);
        }

        if (hostView == null) {
            Log.d(TAG, "AppWidgetHostView was null for " + componentName);
            return null;
        }
        return hostView;
    }

    public List<Integer> getWidgetIds(ComponentName cn) {
        String flattenComponentName = cn.flattenToString();
        return getWidgetIds(flattenComponentName);
    }

    public List<Integer> getWidgetIds(String flattenComponentName) {
        String strIds = mPrefs.getString(flattenComponentName, null);
        List<Integer> ids = new ArrayList<>();
        if (!TextUtils.isEmpty(strIds)) {
            ids = mGson.fromJson(strIds, new TypeToken<ArrayList<Integer>>() {

            }.getType());
        }
        return ids;
    }


    public void saveWidgetId(ComponentName cn, int id) {
        List<Integer> tempIds = getWidgetIds(cn);
        tempIds.add(id);
        saveWidgetId(cn, tempIds);
    }

    public void saveWidgetId(ComponentName cn, List<Integer> ids) {
        mPrefs.edit().putString(cn.flattenToString(), mGson.toJson(ids)).apply();
    }

    public void removeWidget(ComponentName cn, int id) {
        mAppWidgetHost.deleteAppWidgetId(id);
        List<Integer> ids = getWidgetIds(cn);
        if (ids.contains(id)) {
            ids.remove((Integer) id);
        }
        saveWidgetId(cn, ids);
    }


    public void updateWidgetId(int oldId, int newId) {
        AppWidgetProviderInfo provider = mAppWidgetManager.getAppWidgetInfo(newId);
        saveWidgetId(provider.provider, newId);
        mAppWidgetHost.deleteAppWidgetId(oldId);
    }

    public void done() {
        mAppWidgetHost.stopListening();
    }

    public void delete() {
        mAppWidgetHost.stopListening();
        mAppWidgetHost.deleteHost();
        setupHost();
    }

    public void popupSelectWidget(Activity parent) {
        try {
            int appWidgetId = this.mAppWidgetHost.allocateAppWidgetId();
            Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
            pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            parent.startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
        } catch (Throwable t) {
            Log.e(TAG, t.getMessage(), t);
        }
    }

    private int getAppWidgetIdFromIntent(Intent data) {
        Bundle extras = data.getExtras();
        if (extras == null) return -1;
        return extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);

    }

    private AppWidgetHostView createWidget(Intent data) {
        int appWidgetId = getAppWidgetIdFromIntent(data);
        return createWidgetFromId(appWidgetId);
    }

    private AppWidgetHostView createWidgetFromId(int widget_id) {
        AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widget_id);
        AppWidgetHostView hostView = mAppWidgetHost.createView(mContext, widget_id, appWidgetInfo);
        hostView.setAppWidget(widget_id, appWidgetInfo);
        return hostView;
    }

    private AppWidgetHostView loadWidget(Activity activity, ComponentName cn) {
        final List<AppWidgetProviderInfo> infos = mAppWidgetManager.getInstalledProviders();
        AppWidgetProviderInfo appWidgetInfo = null;
        for (final AppWidgetProviderInfo info : infos) {
            if (info.provider.equals(cn)) {
                // found installed providers
                appWidgetInfo = info;
                break;
            }
        }
        if (appWidgetInfo == null) {
            Log.d(TAG, "app info was null");
            return null;
        }
        return loadWidget(activity, appWidgetInfo);
    }

    private AppWidgetHostView loadWidget(Activity activity, AppWidgetProviderInfo appWidgetInfo) {
        int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
        if (checkBindPermission(activity, appWidgetId, appWidgetInfo)) {
            return null;
        }
        Log.d(TAG, "Allowed to bind");
        Log.d(TAG, "creating widget");
        AppWidgetHostView hostView = mAppWidgetHost.createView(mContext, appWidgetId, appWidgetInfo);
        hostView.setAppWidget(appWidgetId, appWidgetInfo);
        return hostView;
    }

    private boolean checkBindPermission(final Activity activity, final int appWidgetId, final AppWidgetProviderInfo appWidgetInfo) {
        try {
            boolean allowed_to_bind = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, appWidgetInfo.provider);
            if (!allowed_to_bind) {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        Log.d(TAG, "asking for permission");
                        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
                        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
                        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, appWidgetInfo.provider);
                        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, appWidgetInfo.getProfile());
                        addEmptyData(intent);
                        activity.startActivityForResult(intent, REQUEST_BIND_APPWIDGET);

                    }
                }, 500);
                return true;
            }
        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e);
            return false;
        }
        return false;
    }

    private AppWidgetHostView configureWidget(Activity parent, Intent data) {
        try {
            int appWidgetId = getAppWidgetIdFromIntent(data);
            return configureWidget(parent, appWidgetId);
        } catch (Exception | Error e) {
            Log.e(TAG, e.getMessage(), e);
        }
        return null;
    }

    public AppWidgetHostView configureWidget(Activity parent, int appWidgetId) {
        return createWidgetFromId(appWidgetId);
    }

    public ComponentName getConfigure(int appWidgetId) {
        AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
        if (appWidgetInfo == null) return null;
        return appWidgetInfo.configure;
    }

    private void addEmptyData(Intent pickIntent) {
        ArrayList<Parcelable> customInfo = new ArrayList<>();
        pickIntent.putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo);
        ArrayList<Parcelable> customExtras = new ArrayList<>();
        pickIntent.putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras);
    }

//    public AppWidgetHostView onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
//
//        Log.d(TAG, "onActivityResult: requestCode=" + requestCode + " resultCode=" + resultCode);
//        // listen for widget manager response
//        if (resultCode == Activity.RESULT_OK) {
//            if (requestCode == REQUEST_PICK_APPWIDGET || requestCode == REQUEST_BIND_APPWIDGET) {
//
//                Log.d(TAG, "configureWidget");
//
//            } else {
//                Log.d(TAG, "unknown RESULT_OK");
//            }
//        }
//        return null;
//    }


    public boolean addWidget(AppWidgetHostView appWidgetHostView) {
        boolean addedSuccess = false;
        Log.d(TAG, "addWidget appWidgetHostView " + appWidgetHostView);
        ComponentName cn = appWidgetHostView.getAppWidgetInfo().provider;
        saveWidgetId(cn, appWidgetHostView.getAppWidgetId());
        addedSuccess = true;
        return addedSuccess;
    }

    public boolean addWidget(AppWidgetHostView appWidgetHostView, ComponentName componentName) {
        boolean addedSuccess = false;
        Log.d(TAG, "addWidget appWidgetHostView " + appWidgetHostView);
        saveWidgetId(componentName, appWidgetHostView.getAppWidgetId());
        addedSuccess = true;
        return addedSuccess;
    }

    public List<Integer> getAllAddedWidgetIds() {
        List<Integer> lists = new ArrayList<>();
        Map<String, Integer> map = (Map<String, Integer>) mPrefs.getAll();
        if (map != null && map.size() > 0) {
            for (Integer value : map.values()) {
                lists.add(value);
            }
        }
        return lists;
    }

    public List<String> getAllAddedWidgetComponentName() {
        List<String> lists = new ArrayList<>();
        Map<String, Integer> map = (Map<String, Integer>) mPrefs.getAll();
        if (map != null && map.size() > 0) {
            for (String key : map.keySet()) {
                lists.add(key);
            }
        }
        return lists;
    }

    public void stopListening() {
        Log.e(TAG, "stopListening");
        if (mAppWidgetHost != null) {
            mAppWidgetHost.stopListening();
        }
    }

    /**
     * get all install provider
     */
    private List<AppWidgetProviderInfo> getAllProvider() {
        List<AppWidgetProviderInfo> appWidgetProviderInfos = mAppWidgetManager.getInstalledProviders();
        for (AppWidgetProviderInfo appWidgetProviderInfo : appWidgetProviderInfos) {
            Log.d("xxx", appWidgetProviderInfo.provider.getClassName() + "//" + appWidgetProviderInfo.provider.getPackageName());
        }
        return appWidgetProviderInfos;
    }

    private AppWidgetProviderInfo getLauncherNeededWidgetProvider() {
        AppWidgetProviderInfo appWidgetProviderInfo = null;
        List<AppWidgetProviderInfo> appWidgetProviderInfos = getAllProvider();
        String targetPackageName = "com.example.widgetappone";
        String targetClassName = "com.example.widgetappone.AppWidgetProvider";
        ComponentName targetComponentName = ComponentName.createRelative(targetPackageName, targetClassName);
        for (AppWidgetProviderInfo tempAppWidgetProviderInfo : appWidgetProviderInfos) {
            ComponentName componentName = tempAppWidgetProviderInfo.provider;
            if (targetComponentName.equals(componentName)) {
                appWidgetProviderInfo = tempAppWidgetProviderInfo;
                break;
            }
        }
        return appWidgetProviderInfo;
    }

    /**
     * add default widget if needed
     */
    public void addDefaultWidget(WidgetsContainer widgetsContainer) {
        int newAppWidgetId = mAppWidgetHost.allocateAppWidgetId();
        AppWidgetProviderInfo appWidgetProviderInfo = getLauncherNeededWidgetProvider();
        ComponentName componentName = appWidgetProviderInfo.provider;
//        setBindAppWidgetPermission("com.example.widgetappone");
        mAppWidgetManager.setBindAppWidgetPermission("com.example.widgetappone", true);
        boolean allowedBound = mAppWidgetManager.bindAppWidgetIdIfAllowed(newAppWidgetId, componentName);
        Log.d(TAG, "addDefaultWidget allowed add appwidget? " + allowedBound + "//newAppWidgetId " + newAppWidgetId);
        if (allowedBound) {
            AppWidgetHostView appWidgetHostView = createWidgetFromId(newAppWidgetId);
            addWidget(appWidgetHostView, componentName);
            widgetsContainer.addWidgetToScreen(appWidgetHostView);
        }
    }

    public void setBindAppWidgetPermission(String packageName) {
        try {
            Class localClass = mAppWidgetManager.getClass();
            Class[] arrayOfClass = new Class[2];
            arrayOfClass[0] = String.class;
            arrayOfClass[1] = Boolean.TYPE;
            Method localMethod = localClass.getMethod("setBindAppWidgetPermission",
                    arrayOfClass);
            Object[] arrayOfObject = new Object[2];
            arrayOfObject[0] = packageName;
            arrayOfObject[1] = Boolean.valueOf(true);
            localMethod.setAccessible(true);//控制java的访问控制检查
            localMethod.invoke(mAppWidgetManager, arrayOfObject);
            return;
        } catch (Exception localException) {
            localException.printStackTrace();
        }
    }

    public void confirmWidgetPermission() {
        int newAppWidgetId = mAppWidgetHost.allocateAppWidgetId();
        AppWidgetProviderInfo appWidgetProviderInfo = getLauncherNeededWidgetProvider();
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND)
                .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, newAppWidgetId)
                .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, appWidgetProviderInfo.provider)
                .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, appWidgetProviderInfo.getProfile());
        activity.startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
    }
}
